package logger

import (
	"fmt"
	"github.com/pkg/errors"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"runtime"
	"strings"
	"sync"
)

var Logger *zap.Logger
var mux sync.Mutex

type logLevel string

const (
	lvDebug logLevel = "D"
	lvInfo           = "I"
	lvWarn           = "W"
	lvError          = "E"
	lvFatal          = "F"
)

//Info 一般性的日志信息
var Info = func(v ...interface{}) {
	doLog(Logger.Info, lvInfo, 2, v...)
}

//Infof 一般性的日志信息
var Infof = func(format string, v ...interface{}) {
	doLogf(Logger.Info, lvInfo, 2, format, v...)
}

//Warn 会出现潜在错误的情形。
var Warn = func(v ...interface{}) {
	doLog(Logger.Warn, lvWarn, 2, v...)
}

//Warnf 会出现潜在错误的情形。
var Warnf = func(format string, v ...interface{}) {
	doLogf(Logger.Warn, lvWarn, 2, format, v...)
}

//Error 其他运行时错误或意外情况。
var Error = func(v ...interface{}) {
	doLog(Logger.Error, lvError, 2, v...)
}

//Errorf 其他运行时错误或意外情况。
var Errorf = func(format string, v ...interface{}) {
	doLogf(Logger.Error, lvError, 2, format, v...)
}

//Fatal 导致应用程序提前终止的严重错误。
var Fatal = func(v ...interface{}) {
	doLog(Logger.Fatal, lvFatal, 2, v...)
}

//Fatalf 导致应用程序提前终止的严重错误。
var Fatalf = func(format string, v ...interface{}) {
	doLogf(Logger.Fatal, lvFatal, 2, format, v...)
}

//Debug 仅调试用
var Debug = func(v ...interface{}) {
	doLog(Logger.Debug, lvDebug, 2, v...)
}

//Debugf 仅调试用
var Debugf = func(format string, v ...interface{}) {
	doLogf(Logger.Debug, lvDebug, 2, format, v...)
}

//Panicf 导致应用程序提前终止的严重错误。 -- 需要出严重告警
var Panicf = func(format string, v ...interface{}) {
	doLogf(Logger.Panic, lvError, 2, format, v...)
}

func doLog(logFunc func(msg string, fields ...zap.Field), l logLevel, depth int, v ...interface{}) {
	//调用函数名: funcName : runtime.FuncForPC(funcName).Name()
	pc, file, line, ok := runtime.Caller(depth)
	if !ok {
		file = "???"
		line = 1
	} else {
		slash := strings.LastIndex(file, "/")
		if slash >= 0 {
			file = file[slash+1:]
		}
	}
	//文件名:行数:函数名
	logStr := fmt.Sprintf("[%s:%d:%s] [%s] %v",
		file, line, getCurrentFunName(runtime.FuncForPC(pc).Name()), l,
		v)
	logFunc(logStr)
}

func doLogf(logFunc func(msg string, fields ...zap.Field), l logLevel, depth int, format string, v ...interface{}) {
	//调用函数名: funcName : runtime.FuncForPC(funcName).Name()
	pc, file, line, ok := runtime.Caller(depth)
	if !ok {
		file = "???"
		line = 1
	} else {
		slash := strings.LastIndex(file, "/")
		if slash >= 0 {
			file = file[slash+1:]
		}
	}

	logStr := fmt.Sprintf("[%s:%d:%s] [%s] %s",
		file, line, getCurrentFunName(runtime.FuncForPC(pc).Name()), l,
		fmt.Sprintf(format, v...))

	logFunc(logStr)
}

//getCurrentFunName 从函数调用全路径中得到函数名
func getCurrentFunName(path string) string {
	// 保证闭包函数名也能正确显示 如TestErrorf.func1:
	idx := strings.LastIndexByte(path, '/')
	if idx != -1 {
		path = path[idx:]
		idx = strings.IndexByte(path, '.')
		if idx != -1 {
			path = strings.TrimPrefix(path[idx:], ".")
		}
	}
	return path
}

func init() {
	if Logger == nil {
		mux.Lock()
		if Logger == nil {
			Logger, _ = NewDefaultLogger()
		}
		defer mux.Unlock()
	}
}

// NewDefaultLogger creates a new logger with our prefered options
func NewDefaultLogger() (*zap.Logger, error) {
	if Logger != nil {
		return Logger, nil
	}
	config := zap.Config{
		Development:       true,
		DisableCaller:     false,
		DisableStacktrace: false,
		EncoderConfig:     zap.NewDevelopmentEncoderConfig(),
		Encoding:          "json",
		ErrorOutputPaths:  []string{"stdout"},
		Level:             zap.NewAtomicLevel(),
		OutputPaths:       []string{"stdout"},
	}
	config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	l, err := config.Build()
	if err != nil {
		return nil, errors.Wrap(err, "failed to create logger")
	}
	Logger = l
	return l, nil
}
